package com.mindolph.mindmap.extension.exporters.branch;

import com.igormaznitsa.mindmap.model.*;
import com.mindolph.mindmap.I18n;
import com.mindolph.mindmap.extension.ContextMenuSection;
import com.mindolph.mindmap.extension.api.ExtensionContext;
import com.mindolph.mindmap.extension.exporters.BaseLiteralExportExtension;
import com.mindolph.mindmap.model.TopicNode;
import com.mindolph.mindmap.util.DialogUtils;
import com.mindolph.mindmap.util.MindMapUtils;
import javafx.scene.text.Text;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @since 1.8
 */
public class AsciiDocBranchExporter extends BaseLiteralExportExtension {

    private static final Logger log = LoggerFactory.getLogger(AsciiDocBranchExporter.class);

    @Override
    public void doExport(ExtensionContext context, List<Boolean> options, String exportFileName, OutputStream out) throws IOException {
        super.includeAttributes = options.getFirst();
        this.doConvertingAndSave(context.getModel(), context.getSelectedTopics(), exportFileName, out);
    }

    protected void doConvertingAndSave(MindMap<TopicNode> model, List<TopicNode> topics, String exportFileName, OutputStream out) throws IOException {
        String text = this.convertTopics(model, topics);
        File fileToSave = null;
        if (out == null) {
            fileToSave = DialogUtils.selectFileToSaveForFileFilter(
                    I18n.getIns().getString("ASCIIDOCExporter.saveDialogTitle"),
                    null,
                    ".asciidoc",
                    I18n.getIns().getString("ASCIIDOCExporter.filterDescription"),
                    exportFileName);
            fileToSave = MindMapUtils.checkFileAndExtension(fileToSave, ".asciidoc");
            out = fileToSave == null ? null : new BufferedOutputStream(new FileOutputStream(fileToSave, false));
        }
        if (out != null) {
            try {
                IOUtils.write(text, out, StandardCharsets.UTF_8);
            } finally {
                if (fileToSave != null) {
                    IOUtils.closeQuietly(out);
                }
            }
        }
    }

    @Override
    protected String convertTopics(MindMap<TopicNode> model, List<TopicNode> topics) {
        StringBuilder buf = new StringBuilder();
        buf.append("// Generated by Mindolph AsciiDoc exporter https://github.com/mindolph/Mindolph").append(Constants.NEXT_LINE);
        topics = TopicUtils.removeDuplicatedAndDescendants(topics);
        for (TopicNode selectedTopic : topics) {
            model.traverseTopicTree(selectedTopic, topicNode -> {
                try {
                    buf.append(this.convertTopic(topicNode, selectedTopic.getTopicLevel()));
                } catch (IOException e) {
                    log.warn("", e);
                }
            });
        }
        return buf.toString();
    }

    public String convertTopic(TopicNode topic, int baseLevel) throws IOException {
        StringBuilder buf = new StringBuilder();
        int level = topic.getTopicLevel() - baseLevel;
        String uid = TopicUtils.getTopicUid(topic);

        if (uid != null) {
            buf.append("anchor:").append(uid).append("[]").append(Constants.NEXT_LINE).append(Constants.NEXT_LINE);
        }
        String prefix = StringUtils.repeat('=', level + 1);
        buf.append(prefix).append(' ').append(topic.getText().replace("\n", " pass:[<br>]")).append(Constants.NEXT_LINE);

        if (level == 0) {
            buf.append(":encoding: UTF-8").append(Constants.NEXT_LINE);
            buf.append(":Date: ").append(DATE_FORMAT.format(new Date())).append(Constants.NEXT_LINE);
        }
        buf.append(Constants.NEXT_LINE);

        if (!includeAttributes) {
            return buf.toString();
        }

        ExtraFile file = (ExtraFile) TopicUtils.findExtra(topic, Extra.ExtraType.FILE);
        ExtraLink link = (ExtraLink) TopicUtils.findExtra(topic, Extra.ExtraType.LINK);
        ExtraNote note = (ExtraNote) TopicUtils.findExtra(topic, Extra.ExtraType.NOTE);
        ExtraTopic transition = (ExtraTopic) TopicUtils.findExtra(topic, Extra.ExtraType.TOPIC);

        if (note != null) {
            for (String s : StringUtils.split(note.getValue(), Constants.NEXT_LINE)) {
                buf.append(s).append(" +");
                buf.append(Constants.NEXT_LINE);
            }
            buf.append(Constants.NEXT_LINE);
        }

        if (file != null) {
            MMapURI fileURI = file.getValue();
            String filePathAsText =
                    fileURI.isAbsolute() ? fileURI.asFile(null).getAbsolutePath() : fileURI.toString();
            buf.append("link:++").append(filePathAsText).append("++[File]").append(Constants.NEXT_LINE)
                    .append(Constants.NEXT_LINE);
        }

        if (link != null) {
            String url = link.getValue().toString();
            String ascurl = link.getValue().asString(true, true);
            buf.append("link:").append(ascurl).append("[Link]").append(Constants.NEXT_LINE).append(Constants.NEXT_LINE);
        }

        if (transition != null) {
            TopicNode linkedTopic = topic.getMap().findTopicForLink(transition);
            if (linkedTopic != null) {
                buf.append("<<").append(TopicUtils.getTopicUid(linkedTopic)).append(",Go to>>").append(Constants.NEXT_LINE).append(Constants.NEXT_LINE);
            }
        }

        for (Map.Entry<String, String> s : topic.getCodeSnippets().entrySet()) {
            buf.append("[source,").append(s.getKey()).append("]").append(Constants.NEXT_LINE);
            buf.append("----").append(Constants.NEXT_LINE);
            buf.append(s.getValue());
            if (buf.charAt(buf.length() - 1) != '\n') {
                buf.append(Constants.NEXT_LINE);
            }
            buf.append("----").append(Constants.NEXT_LINE).append(Constants.NEXT_LINE);
        }
        return buf.toString();
    }

    @Override
    public ContextMenuSection getSection() {
        return ContextMenuSection.EXPORT_BRANCHES;
    }

    @Override
    public String getName(ExtensionContext context, TopicNode activeTopic) {
        return I18n.getIns().getString("ASCIIDOCExporter.exporterName");
    }

    @Override
    public String getReference(ExtensionContext context, TopicNode activeTopic) {
        return "Export branches as AsciiDoc file";
    }

    @Override
    public Text getIcon(ExtensionContext context, TopicNode activeTopic) {
        return null;
    }

    @Override
    public int getOrder() {
        return 4;
    }

    @Override
    public boolean needsTopicUnderMouse() {
        return true;
    }
}
